Skip to content

feat(share): point the app at a self-hosted share/collab server at runtime - #1687

Merged
giswqs merged 7 commits into
mainfrom
feat/runtime-share-host
Aug 4, 2026
Merged

feat(share): point the app at a self-hosted share/collab server at runtime#1687
giswqs merged 7 commits into
mainfrom
feat/runtime-share-host

Conversation

@giswqs

@giswqs giswqs commented Aug 3, 2026

Copy link
Copy Markdown
Member

Closes #1684. First step of #1665's self-hosting track.

Problem

The web container could not be pointed at a self-hosted sharing server or collaboration relay.

resolveShareBaseUrl() and resolveCollabBaseUrl() already honored VITE_GEOLIBRE_SHARE_URL / VITE_GEOLIBRE_COLLAB_URL — but both read import.meta.env, so they were build-time only, and neither variable appeared in Dockerfile or docker/entrypoint.sh. Repointing the published image meant forking and rebuilding it.

There was also a worse failure mode. resolveShareBaseUrl() fell back to https://share.geolibre.app whenever the configured value failed to parse or was HTTP on a non-loopback host. A self-hosted deployment that set http://geolibre.lan:8080, or typo'd its own hostname, got a working Share button that uploaded its users' projects to the public hosted service.

What changed

The entrypoint already writes geolibre-runtime-config.js on every boot (for the AI proxy and embed origins), so both hosts now flow through that same channel:

lib/deployment-env.ts New. Reads one VITE_* value, deployment env before build env — the precedence readEmbedOrigins and readDeploymentAssistantEnv already use.
resolveShareHost() New. Returns a status — default / configured / disabled / invalid — plus the base URL and the configured value. resolveShareBaseUrl() is now a thin wrapper returning string | null.
GEOLIBRE_SHARE_URL=off Removes Share and the Project Gallery from the UI entirely.
resolveCollabBaseUrl() Reads the deployment env too, so a prebuilt image can enable collaboration against a self-hosted relay.
docker/entrypoint.sh Carries both vars, validating each and exiting on a malformed value — the same discipline the GEOLIBRE_EMBED_ORIGINS block already applies.
Dockerfile Matching ARG/ENV pairs, so the build-time path is documented rather than accidental.

No more silent fallback. A rejected value resolves to null; the hosted default applies only when nothing is configured. Menu entries disable with a reason, and the gallery throws a new not-configured GalleryErrorCode.

The hostname is no longer hardcoded in the UI. 11 catalogue keys across all 16 locales now take a {{shareHost}} interpolation (the hostname isn't translated, so this was a mechanical substitution), and the account-settings link in SettingsDialog / ShareProjectDialog derives from the resolved host. A self-hosted instance previously read "Sign in to share.geolibre.app" while linking somewhere else.

TLS policy (decision on item 6 of the issue)

Kept as-is and now documented: HTTPS/WSS required, plaintext only on loopback. These URLs carry a Bearer token. The change is that a value failing this now fails the container boot with an error naming the variable, instead of quietly using the public service. Self-hosters put the server behind a TLS-terminating proxy. I did not add the GEOLIBRE_SHARE_ALLOW_INSECURE escape hatch the issue floated — happy to if you'd rather have it.

Verification

Beyond the unit tests, I drove the built web app under vite preview with the runtime config set four ways (clearing the service worker between each, since it precaches the config):

Config Result
unset Unchanged. Share enabled, copy names share.geolibre.app
off Share and Gallery absent from the Project menu
http://internal.corp Both present, aria-disabled=true, title "Unavailable: this deployment's sharing server address is not valid."
https://maps.example.org Share enabled; copy reads "Sign in to maps.example.org"; "Get API token" opens https://maps.example.org/settings

Also exercised the entrypoint's validator across 12 inputs — https, off/OFF, loopback http, wss, loopback ws all accepted; plaintext LAN, unparseable, embedded credentials, and wrong-scheme all exit with a message naming the variable.

Gate: npm run build, npm run test:frontend (4959 pass / 0 fail), npm run test:worker, npm run lint (0 errors; the two TopToolbar warnings are pre-existing on main, verified by stashing).

Notes for review

  • resolveShareBaseUrl() changing from string to string | null is the deliberate core of this. Four call sites handle null; the tests that asserted the old fallback now assert refusal.
  • Desktop is unchanged in reach: installNativeShareFetch skips the override when null, but the Tauri http:default capability scope still pins the share host, so self-hosting stays a web/Docker capability. Called out in share-fetch.ts and the docs.
  • pre-commit run could not install its node hook env locally (npm error Unknown cli flag: --ignore-prepublish — a toolchain issue on my machine, not this branch), so I ran the hook equivalents directly: oxfmt@0.59.0 over every changed file, plus eslint and the build via the npm scripts. pre-commit.ci will run the rest on the PR.
  • Non-English catalogues got the placeholder substituted but are otherwise untouched; no translation drifted.

Summary by CodeRabbit

  • New Features

    • Configure project sharing and collaboration services per deployment at build time or runtime.
    • Support custom self-hosted endpoints with secure URL validation.
    • Sharing and Project Gallery controls now reflect service availability.
  • Bug Fixes

    • Prevented requests and uploads when services are unavailable or invalid.
    • Updated translated messages to display the configured sharing host.
  • Documentation

    • Added self-hosting guidance for sharing and collaboration configuration.

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Point the web container at a self-hosted share and collaboration server at runtime, and never silently fall back to the public host

2 participants